package com.gamefx.resource
{
	import flash.events.Event;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.LocalConnection;
	import flash.system.Capabilities;
	import flash.utils.getTimer;

	/**
	 * LoaderCore控制整个应用程序的网络并发连接数
	 * 
	 * @author hzf
	 * 
	 */
	internal class LoaderCore extends LoaderGroup
	{
		private static var instance:LoaderCore;

		private var threads:Vector.<LoaderProcess>;
		private var speedTick:Array = [];
		private var lastTime:int;
		

		
		public function LoaderCore()
		{
			if (instance != null)
			{
				throw Error("LoaderCore Singleton already constructed!");
			}
			
			instance = this;

			threads = new Vector.<LoaderProcess>();
			
			//alt method (Capabilities.playerType != "ActiveX" && Capabilities.playerType != "PlugIn") doesn't work when testing locally in an html wrapper
			LoaderItem._isLocal = Boolean(Capabilities.playerType == "Desktop" || (new LocalConnection( ).domain == "localhost"));

		}
		
		public static function getInstance():LoaderCore
		{
			if (instance == null)
			{
				instance = new LoaderCore();
			}
			
			return instance;
		}
		
		
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------

		/**
		 * 
		 */
		private var _maxConnections:uint = 5;

		public function get maxConnections():uint
		{
			return _maxConnections;
		}

		public function set maxConnections(value:uint):void
		{
			if (value < 1)
			{
				throw new Error("maxConnections can't less than 1");
			}
			_maxConnections = value;
		}
		
		
		/**
		 * 
		 */
		private var _activeCount:uint;

		public function get activeCount():uint
		{
			return _activeCount;
		}
		
		
		/**
		 * 
		 */
		private var _connectedCount:uint;

		public function get connectedCount():uint
		{
			return _connectedCount;
		}

		
		/**
		 * 
		 */
		private var _networkSpeed:Number = 0;

		public function get networkSpeed():Number
		{
			return _networkSpeed;
		}

		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		override public function load():void
		{
			if (status == LoaderStatus.LOADING)
			{
				return;
			}
			
			status = LoaderStatus.LOADING;
			loadNext();
		}
		
		override public function cancel():void
		{
			status = LoaderStatus.PAUSED;
		}
		
		override public function insert(loader:LoaderItem, index:uint = 999999999):LoaderItem
		{
			super.insert(loader, index);
			
			loadNext();
			return loader;
		}
		
		
		private function loadNext():void
		{
			if (status == LoaderStatus.PAUSED)
			{
				return;
			}
			
			if (_activeCount >= maxConnections)
			{
				return;
			}

			var loader:LoaderItem = shiftNextLoader();
			if (loader == null)
			{
				return;
			}

			var thread:LoaderProcess;
			if (threads.length > 0)
			{
				thread = threads.shift();
			}
			else
			{
				thread = createNewStream();
			}
			
			thread.process(loader);
			_activeCount++;
			
			loadNext();
		}
		
		private function closeAndLoadNext(thread:LoaderProcess):void
		{
			thread.close();
			thread.loader = null;
			
			if (threads.length < _maxConnections)
			{
				threads.push(thread);
			}
			else
			{
				destoryStream(thread);
			}
			
			loadNext();
		}
		
		private function calcNetSpeed():void
		{
			speedTick.push( {bytes:bytesLoaded, time:getTimer()} );			
			if (speedTick.length > 50)
			{
				speedTick.shift();
			}
			
			if (speedTick.length > 5 && (speedTick[speedTick.length-1].time - lastTime) > 500)
			{
				lastTime = speedTick[speedTick.length-1].time;
				_networkSpeed = Math.max(0, Math.round((speedTick[speedTick.length-1].bytes - speedTick[0].bytes) / (speedTick[speedTick.length-1].time - speedTick[0].time)));
			}
		}
		
		private function createNewStream():LoaderProcess
		{
			var thread:LoaderProcess = new LoaderProcess();
			thread.addEventListener(Event.OPEN, this.onOpen);
			thread.addEventListener(Event.COMPLETE, this.onComplete);
			thread.addEventListener(ProgressEvent.PROGRESS, this.onProgress);
			thread.addEventListener(HTTPStatusEvent.HTTP_STATUS, this.onHTTPStatus);
			thread.addEventListener(IOErrorEvent.IO_ERROR, this.onIOError);
			thread.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onComplete);
			
			return thread;
		}
		
		private function destoryStream(thread:LoaderProcess):void
		{
			thread.removeEventListener(Event.OPEN, this.onOpen);
			thread.removeEventListener(Event.COMPLETE, this.onComplete);
			thread.removeEventListener(ProgressEvent.PROGRESS, this.onProgress);
			thread.removeEventListener(HTTPStatusEvent.HTTP_STATUS, this.onHTTPStatus);
			thread.removeEventListener(IOErrorEvent.IO_ERROR, this.onIOError);
			thread.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onComplete);
		}
				
		//--------------------------------------------------------------------------
		//
		//  Event handlers
		//
		//--------------------------------------------------------------------------
		
		override internal function onOpen(event:Event):void
		{
			var thread:LoaderProcess = event.currentTarget as LoaderProcess;
			thread.loader.onOpen(event);
			
			_connectedCount++;
		}

		override internal function onComplete(event:Event):void
		{
			_activeCount--;		
			_connectedCount--;			
			
			var thread:LoaderProcess = event.currentTarget as LoaderProcess;
			thread.loader.onComplete(event);

			closeAndLoadNext(thread);
		}

		override internal function onProgress(event:ProgressEvent):void
		{
			var thread:LoaderProcess = event.currentTarget as LoaderProcess;
			thread.loader.onProgress(event);
			
			calcNetSpeed();
		}

		override internal function onHTTPStatus(event:HTTPStatusEvent):void
		{
			var thread:LoaderProcess = event.currentTarget as LoaderProcess;
			thread.loader.onHTTPStatus(event);
		}
		
		override internal function onIOError(event:IOErrorEvent):void
		{
			_activeCount--;		
			_connectedCount--;			

			var thread:LoaderProcess = event.currentTarget as LoaderProcess;
			thread.loader.onIOError(event);
			
			closeAndLoadNext(thread);
		}
		
		override internal function onSecurityError(event:SecurityErrorEvent):void
		{
			_activeCount--;		
			_connectedCount--;			

			var thread:LoaderProcess = event.currentTarget as LoaderProcess;
			thread.loader.onSecurityError(event);
			
			closeAndLoadNext(thread);
		}
	}
}
